#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>

namespace HttpProtocol
{
    const std::string HttpSep = "\r\n";
    const std::string wwwroot = "./wwwroot";
    const std::string homepage = "index.html";
    class HttpRequest
    {
    public:
        HttpRequest()
            : _req_blank(HttpSep)
        {
        }

        bool Getline(std::string &request, std::string *line)
        {
            int pos = request.find(HttpSep);
            if (pos == std::string::npos)
                return false;
            *line = request.substr(0, pos);
            request.erase(0, pos + HttpSep.size());
            return true;
        }

        void Deserialize(std::string &request)
        {
            std::string line;
            bool ok = Getline(request, &line);
            if (!ok)
                return;
            _requestline = line;

            while (true)
            {
                bool ok = Getline(request, &line);
                if (ok && !line.empty())
                {
                    _req_header.push_back(line);
                }
                else if (ok && line.empty())
                {
                    _req_content = request;
                    break;
                }
                else
                {
                    break;
                }
            }
            return;
        }
        void ParseReqLine()
        {
            std::stringstream ss(_requestline);
            ss >> _req_method >> _req_url >> _req_httpVersion;
            if (_req_url == "/")
            {
                _path = wwwroot + "/" + homepage;
            }
            else
            {
                _path = wwwroot + _req_url;
            }
        }

        void ParseSuffix()
        {
            int pos = _req_url.rfind(".");
            if (pos == std::string::npos)
                _suffix = ".html";
            else
                _suffix = _req_url.substr(pos);
        }

        void Parse()
        {
            ParseReqLine();
            ParseSuffix();
        }

        void DebugPrint()
        {
            std::cout << "ReqLine: " << _requestline << std::endl;
            for (auto str : _req_header)
            {
                std::cout << "------> " << str << std::endl;
            }
            std::cout << "req_blank: " << _req_blank << std::endl;
            std::cout << "_req_content: " << _req_content << std::endl;
            std::cout << "_req_method: " << _req_method << std::endl;
            std::cout << "_req_url: " << _req_url << std::endl;
            std::cout << "_req_httpVersion: " << _req_httpVersion << std::endl;
        }

        std::string GetFileContentHelper(std::string path)
        {
            std::ifstream in(path, std::ifstream::binary);
            if (!in.is_open())
                return "";
            in.seekg(0, in.end);
            int filesize = in.tellg();
            in.seekg(0, in.beg);

            std::string content;
            content.resize(filesize);
            in.read((char *)content.c_str(), filesize);
            in.close();
            return content;
        }

        std::string GetFileContent()
        {
            return GetFileContentHelper(_path);
        }

        std::string GetSuffix()
        {
            return _suffix;
        }

        std::string Get404()
        {
            return GetFileContentHelper("./wwwroot/404.html");
        }

        std::string GetUrl()
        {
            return _req_url;
        }

    private:
        std::string _requestline; // method url httpversion
        std::vector<std::string> _req_header;
        std::string _req_blank;
        std::string _req_content;

        std::string _req_method;
        std::string _req_url;
        std::string _req_httpVersion;
        std::string _path;
        std::string _suffix;
    };

    const std::string BlankSep = " ";
    const std::string lineSep = "\r\n";
    class HttpResponse
    {
    public:
        HttpResponse()
            : _http_serversion("HTTP/1.0"), _status_code(200), _status_code_desc("ok"), _resp_black(lineSep)
        {
        }

        void SetCode(int status_code)
        {
            _status_code = status_code;
        }

        void SetDesc(const std::string &desc)
        {
            _status_code_desc = desc;
        }

        void AddHeader(const std::string &header)
        {
            _resp_header.push_back(header);
        }

        void AddContent(const std::string &content)
        {
            _resp_content = content;
        }

        void MakeStatusLine()
        {
            _status_line = _http_serversion + BlankSep + std::to_string(_status_code) + BlankSep + _status_code_desc + lineSep;
        }

        std::string Serialize()
        {
            std::string content = _status_line;
            for (auto &header : _resp_header)
            {
                content += header + lineSep;
            }
            content += _resp_black;
            content += _resp_content;
            return content;
        }

    private:
        std::string _status_line;
        std::vector<std::string> _resp_header;
        std::string _resp_black;
        std::string _resp_content;

        std::string _http_serversion;
        int _status_code;
        std::string _status_code_desc;
    };
}